package com.wh1200.mybatis.xmlless.config

import com.wh1200.mybatis.SpringTypeResolver
import com.wh1200.mybatis.XmllessAnnotationHandler
import com.wh1200.mybatis.xmlless.JavaClassWrapper
import com.wh1200.mybatis.xmlless.TypeRegistry
import com.wh1200.mybatis.xmlless.XmlLessMapper
import com.wh1200.mybatis.xmlless.enums.XmllessDbType
import com.wh1200.mybatis.xmlless.model.FieldMappingList
import com.wh1200.mybatis.xmlless.model.MethodInfo
import com.wh1200.mybatis.xmlless.model.ResolvedQuery
import com.wh1200.mybatis.xmlless.model.XmllessTableInfo
import com.wh1200.mybatis.xmlless.model.dto.DefaultMethodWrapper
import com.wh1200.mybatis.xmlless.resolver.MappingResolver
import com.wh1200.mybatis.xmlless.resolver.QueryResolver
import com.wh1200.mybatis.xmlless.util.BuilderAssistantHolder
import com.wh1200.mybatis.xmlless.util.TableInfoProvider
import com.wh1200.mybatis.xmlless.util.XmllessInfoUtil
import com.baomidou.mybatisplus.core.config.GlobalConfig
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils
import com.wh1200.mybatis.xmlless.util.DbTypeHolder
import org.apache.ibatis.builder.MapperBuilderAssistant
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.TestInstance
import org.springframework.core.ResolvableType
import java.lang.reflect.Method

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
open class BaseResolverTest(
    private val mapperClass: Class<out XmlLessMapper<*>>,
    val dbType: XmllessDbType = XmllessDbType.POSTGRE_SQL
) {

  protected val modelClass = resolveModelClass()
  protected val mappings: FieldMappingList
  private val configuration = MybatisXmlLessConfiguration(dbType).apply {
    this.isMapUnderscoreToCamelCase = true
    val globalConfig = GlobalConfig()
    globalConfig.annotationHandler = XmllessAnnotationHandler()
    globalConfig.dbConfig = GlobalConfig.DbConfig()
    GlobalConfigUtils.setGlobalConfig(this, globalConfig)
  }
  private val resource = modelClass.name.replace('.', '/') + ".java (best guess)"
  private val currentNameSpace = "np"
  protected val builderAssistant = MapperBuilderAssistant(configuration, resource).apply {
    currentNamespace = currentNameSpace
  }
  private val tableInfo: XmllessTableInfo

  private fun resolveModelClass(): Class<*> {
    val type = ResolvableType.forClass(mapperClass, XmlLessMapper::class.java)
    val inter = type.interfaces.first {
      it.rawClass == XmlLessMapper::class.java
    }
    return inter.generics[0].rawClass!!
  }

  init {
    BuilderAssistantHolder.set(builderAssistant)
    XmllessInfoUtil.registerTypeResolver(SpringTypeResolver())
    tableInfo = createTableInfo(modelClass)
    mappings = MappingResolver.resolve(tableInfo)
    DbTypeHolder.setDbType(dbType)
  }

  @BeforeAll
  open fun init() {
    TypeRegistry.init("com.wh1200.xmlless.test.model")
  }

  protected fun createMethodInfo(method: Method): MethodInfo {
    return QueryResolver.resolveMethodInfo(
        DefaultMethodWrapper(method, mapperClass, modelClass, tableInfo),
        tableInfo,
        dbType
    )
  }

  protected fun createQueryForMethod(method: Method): ResolvedQuery {
    val methodInfo = QueryResolver.resolveMethodInfo(
        DefaultMethodWrapper(
            method,
            mapperClass,
            modelClass,
            tableInfo
        ),
        tableInfo, dbType
    )
    val query = QueryResolver.resolveQuery(methodInfo)
    if (query.query != null) {
      QueryResolver.resolveResultMap(methodInfo, query.query!!)
    }
    return query
  }

  protected fun buildResultMapId(method: Method): String {
    return "$currentNameSpace.${mapperClass.name.replace(".", "_")}_${method.name}"
  }

  protected fun trimLine(str: String): String {
    return str.split("\n").joinToString("\n") { it.trim() }
  }

  private fun createTableInfo(modelClass: Class<*>): XmllessTableInfo {
    return TableInfoProvider.getOrInitXmllessTableInfo(JavaClassWrapper(modelClass))
  }

}
